package conf

import (
	"encoding/json"
	"fmt"
	"github.com/globalsign/mgo"
	"github.com/name5566/leaf/gate"
	remote "github.com/shima-park/agollo/viper-remote"
	"github.com/spf13/viper"
	"io/ioutil"
	"lol.com/feb214-game/nest.git/cache"
	"lol.com/feb214-game/nest.git/leaf"
	"lol.com/feb214-game/nest.git/log"
	"lol.com/feb214-game/nest.git/mg"
	"lol.com/feb214-game/nest.git/proto"
	"lol.com/feb214-game/nest.git/tools"
	"lol.com/feb214-game/nest.git/tools/database"
	"lol.com/feb214-game/nest.git/tools/fs"
	"lol.com/feb214-game/nest.git/tools/mq"
	"lol.com/feb214-game/nest.git/tools/tz"
	"lol.com/feb214-game/nest.git/user"
	"math/rand"
	"net/http"
	"path/filepath"
	"strings"
	"talent.com/cube/conf/g"
	"talent.com/cube/conf/internal"
	"time"
)

var (
	serverToml = &internal.ServerConfigParser{Init: true}
	parsers    = map[string]fs.IConfigParser{
		"server": serverToml,
		"jmg":    &internal.JmgConfigParser{},
		"common": &internal.CmnConfigParser{},
	}

	// 这里是使用远程的配置中心进行配置内容的获取，我先把这里屏蔽了，
	configRemoteParsers = map[string]fs.IConfigParser{
		//"server.toml":  serverToml,
		//"jmg.toml":     &internal.DxtConfigParser{Init: true},
		//"common.toml":  &internal.CmnConfigParser{Init: true},
	}

	fullPathParsers = make(map[string]fs.IConfigParser, len(parsers))
	configBasePath  = tools.GetConfigDir()
)

type StrategyConfNew struct {
	UserId                 uint64
	Rate                   int32     //M比值
	Remark                 string    //备注
	RechargeWithdrawalRate []float64 //充提比区间配置
	RechargeAmount         []int64   //充值金额
	BetAmount              []int64   //下注金额
	BetIndexNum            int       //下注区域控制
	UserDay                []int     //用户活跃天数
	Hit                    bool      //是否触发策略
}

func ServerConfig() *internal.ServerConfig {
	config := parsers["server"].GetConfig()
	return config.(*internal.ServerConfig)
}

func JmgConfig() *internal.JmgConfig {
	config := parsers["jmg"].GetConfig()
	return config.(*internal.JmgConfig)
}

//房间配置
func RoomConfig(gameType, roomKind int32) *internal.RoomConf {
	cs := parsers["common"].GetConfig().(map[int32][]*internal.RoomConf)
	if cs[gameType] == nil || len(cs[gameType]) <= int(roomKind) {
		return nil
	}
	return cs[gameType][roomKind]
}

//机器人配置
func RobotConfig(gameType int32) *internal.RobotConfig {
	switch gameType {
	case g.Jmg:
		return JmgConfig().Robot
	default:
		return nil
	}
}

//各游戏的最大赔付
func GetSysMaxPay(gameType int32) int64 {
	switch gameType {
	case g.Jmg:
		return JmgConfig().Game.SystemMaxPay
	default:
		return JmgConfig().Game.SystemMaxPay
	}
}

//mastro的参数
func GetMastroSubType(gameType int32) string {
	return fmt.Sprintf("cube_%s", g.GetCodeLabel(gameType))
}
func GetMastroThirdType(_ int32, roomKind int32) string {
	return fmt.Sprintf("%v", roomKind)
}

// 初始化服务配置
func InitConfig() {
	var path string
	rand.Seed(time.Now().Unix())

	// 加载各个游戏配置
	for label, parser := range parsers {
		path = filepath.Join(configBasePath, label+".toml")
		parser.ReloadConfig(path, true)
		fullPathParsers[path] = parser
	}

	sc := ServerConfig()
	rc, ic, dc, mc, kc := sc.Redis, sc.IMRedis, sc.DB, sc.Mongo, sc.Kafka
	g.RedisPool = database.NewRedisPool(rc.Host, rc.Password, rc.Index)
	g.IMPool = database.NewRedisPool(ic.Host, ic.Password, ic.Index)
	g.ORM = database.NewMysqlConn(dc.Host, dc.IsDebug)
	g.MG = database.NewMongoSession(mc.Host)
	g.MG.SetMode(mgo.SecondaryPreferred, true)
	g.KafkaPool = mq.NewKafkaClient(kc.Host)

	if len(sc.Apollo.AppId) == 0 {
		remote.SetAppID("new_hundred")
	} else {
		remote.SetAppID(sc.Apollo.AppId)
	}
	remote.SetAppID("new_hundred")
	v := viper.New()
	v.SetConfigType("prop") // 根据namespace实际格式设置对应type
	err := v.AddRemoteProvider("apollo", sc.Apollo.Host, "application")
	if err != nil {
		//TODO
	}
	err = v.ReadRemoteConfig()
	if err != nil {
		//TODO
	}
	g.Viper = v

	go func() {
		for {
			time.Sleep(10 * time.Second)

			err := v.WatchRemoteConfig() // 每次调用该方法，会从apollo缓存接口获取一次配置，并更新viper
			if err != nil {
				panic(err)
			}
		}
	}()

	//配置文件热加载
	if ServerConfig().Server.HotReload {
		tz.Schedule(func() {
			fs.WatchConfigFiles(fullPathParsers)
		}, g.ConfigReloadCycle, g.ServerCloseChan)
	}
}

//用户token过期回调
func onTokenExpired(source []byte) {
	var event cache.TokenExpiredEV
	if err := json.Unmarshal(source, &event); err == nil {
		if v, ok := g.AllAgents.Load(event.UserID); ok {
			ag, _ := v.(gate.Agent)
			leaf.CloseAgent(ag, proto.STATUS_TOKEN_INVALID, "user token expired", event.UserID)
		}
	}
}

//配置更新回调
func onUpdateConf(source []byte) {
	var event cache.ConfUpdateEV
	if err := json.Unmarshal(source, &event); err == nil {
		sc := ServerConfig().Server
		if event.Host != cache.UpdateAllHost && !strings.Contains(sc.URL, event.Host) {
			return
		}
		path := event.Name
		replaceFile := filepath.Join(configBasePath, path)
		if fs.SaveFile(replaceFile, event.Content) {
			if parser, ok := fullPathParsers[replaceFile]; ok {
				parser.ReloadConfig(replaceFile, false)
				log.Info("config file %v reloaded", replaceFile)
			}
		}
	}
}

//公共组件库
//与其他游戏不同，这里每个游戏使用单独的publicCache和orm
func InitDurotar() {
	var (
		cc       *cache.PublicCache
		ac       *user.AccountDAO
		acr      *user.AccountRobotDAO
		gameType int32
	)
	//只初始化激活的游戏
	for _, gameLabel := range ServerConfig().Server.EnabledGame {
		gameType = g.GetLabelCode(gameLabel)
		cc = cache.NewPublicCache(g.IMPool, gameType, g.GetCodeLabel(gameType), ServerConfig().Server.URL)
		ac = &user.AccountDAO{
			ORM:               g.ORM,
			RecordType:        g.Jmg,
			LoseTitle:         fmt.Sprintf("%sbet", g.GetCodeName(gameType)),
			WinTitle:          fmt.Sprintf("%swin", g.GetCodeName(gameType)),
			AllowInsufficient: false, //百人类游戏不允许透支下注
		}
		acr = &user.AccountRobotDAO{
			ORM: g.ORM,
		}
		g.SetPublicCache(gameType, cc)
		g.SetAccountDAO(gameType, ac)
		g.SetAccountRobotDAO(gameType, acr)
	}
	if cc == nil {
		log.Fatal("no enabled game")
		return
	}
	//在任意一个上面注册就行
	cc.RegisterRPCCallback(cache.EventTokenExpired, onTokenExpired)
	cc.RegisterRPCCallback(cache.EventUpdateConf, onUpdateConf)
	cc.StartListenRPCEvent(cache.CasioChannel)
	g.StatsDAO = &mg.UserStatsDAO{Session: g.MG}
	log.Info("init durotar complete")
}

func PullNewConfig() {
	url := fmt.Sprintf(ServerConfig().Server.PullConfUrl, g.GameName)
	resp, err := http.Get(url)
	if err != nil {
		// handle error
		log.Info("FAIL: Pull the newest configs from Conf_Center failed! Try to get it manually！")
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		// handle error
		log.Info("Response content is nil.")
		return
	}
	log.Info("got the newest confs success: %v", string(body))
}

func CloseAll() {
	close(g.ServerCloseChan)
	g.MG.Close()
	_ = g.ORM.Close()
	_ = g.IMPool.Close()
	_ = g.RedisPool.Close()
	_ = g.KafkaPool.Close()
}
